home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1999 March
/
EnigmA AMIGA RUN 35 (1999)(G.R. Edizioni)(IT)[!][issue 1999-03].iso
/
earcd
/
devel
/
vbcc-wos-src
/
pasm
/
eval.c
< prev
next >
Wrap
C/C++ Source or Header
|
1999-01-01
|
22KB
|
828 lines
/* $VER: pasm eval.c V1.2 (21.10.98)
*
* This file is part of pasm, a portable PowerPC assembler.
* Copyright (c) 1997-98 Frank Wille
*
* pasm is freeware and part of the portable and retargetable ANSI C
* compiler vbcc, copyright (c) 1995-98 by Volker Barthelmann.
* pasm may be freely redistributed as long as no modifications are
* made and nothing is charged for it. Non-commercial usage is allowed
* without any restrictions.
* EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
* SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
*
*
* v1.2 (21.10.98) phx
* @sdarx and @sdax are recognized and generate a R_PPC_SDAREL16
* relocation.
* Replaced obsolete R_PPC_TOC16 by real R_PPC_SDAREL16.
* v1.1e (19.10.98) phx
* Improved hihalf(): >> instead /
* v1.0 (04.04.98) phx
* ADDR16-relocation/xref works for instructions, like addi, cmpi, etc.
* v0.9 (07.03.98) phx
* The EHF-offset for R_REL14 is handled in instructions.c. No need
* to increase the return value in makexref() and makereloc().
* v0.7 (01.01.98) phx
* @ha, @h, @l are only allowed at the end of an operand, according
* to GNU-as notation. '@' is only allowed as the first character in
* a symbol (for @function and @object, for example).
* Don't waste memory in makereloc() and makexref() by adding new
* nodes to the current section if another pass is required
* anyway.
* v0.5 (11.10.97) phx
* getarg() works with \' and \" now.
* v0.4 (05.07.97) phx
* R_PPC_TOC16 and R_PPC_REL14 support in makereloc() and makexref().
* "(<term>)@l/h/ha" was not recognized.
* A comment introducer '#' was erroneously treated as a macro
* parameter in read_macro_params().
* Undefined symbols are only automatically declared as externally
* defined, if the -x option was given. Otherwise display an
* error message.
* Implementing backwards-evaluation of an expression in v0.3 was not
* very smart (e.g. 4-1+2 => 1(!)) - changed to forward again... ;)
* v0.3 (20.04.97) phx
* Bug in eval_expression() fixed, which sometimes caused the loss
* of the last argument of an expression.
* v0.2 (25.03.97) phx
* Writes ELF object for 32-bit PowerPC big-endian. Either absolute
* or ELF output format may be selected. ELF is default for all
* currently supported platforms. PPCasm supports nine different
* relocation types (there are much more...).
* Compiles and works also under NetBSD/amiga (68k).
* Changed function declaration to 'new style' in all sources
* (to avoid problems with '...' for example).
* makexref() and makereloc() used wrong offset.
* v0.1 (11.03.97) phx
* First test version with all PowerPC instructions and most
* important directives. Only raw, absolute output.
* v0.0 (21.02.97) phx
* File created.
*/
#define EVAL_C
#include "ppcasm.h"
char *getsymbol(struct GlobalVars *,char *);
char *getarg(struct GlobalVars *,char *);
char *skipspaces(char *);
char *remquotes(char *);
void checkEOL(char *);
char *skipexpression(struct GlobalVars *,char *);
void read_macro_params(struct GlobalVars *,struct ParsedLine *,
struct MacroParams *,char *);
char *getexp(struct GlobalVars *,char *,uint32 *,uint8);
uint32 makereloc(struct GlobalVars *,struct Expression *);
uint32 makexref(struct GlobalVars *,struct Expression *,uint8);
char *getintexp(struct GlobalVars *,char *,uint32 *);
char *eval_expression(struct GlobalVars *,struct Expression *,char *);
static uint32 hihalf(struct GlobalVars *,uint32);
static uint32 lohalf(struct GlobalVars *,uint32);
static uint32 read_hex(char *);
static uint32 read_dec(char *);
static uint32 read_oct(char *);
static uint32 read_bin(char *);
static uint32 read_str(char *);
/* table of valid symbol characters,
0=invalid, 1=valid in whole symbol, 2=valid, but not as first char */
static uint8 valid_symchars[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, /* $ . */
2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,1, /* 0-9 ? */
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* A-O */
1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* P-Z _ */
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* a-o */
1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, /* p-z */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
/* table of valid argument characters,
0=invalid, 1=valid, 2=valid, but indicates string */
static uint8 valid_argchars[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,2,0,1,0,0,2,0,0,0,0,0,0,1,0, /* " $ ' . */
1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1, /* 0-9 ? */
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* A-O */
1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* P-Z _ */
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* a-o */
1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, /* p-z */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
/* valid operators */
#define NUMOPS 10
static char valid_operators[NUMOPS] = {
'+','-','*','/','%','<','>','&','|','^'
};
#define MAXOPPRI 5
static uint8 op_priority[NUMOPS] = {
4,4,5,5,5,3,3,2,0,1
};
char *getsymbol(struct GlobalVars *gv,char *s)
/* read a symbol string into gv->strbuf */
{
uint8 *vsc = valid_symchars;
char *b = gv->strbuf;
int bsize = STRBUFSIZE-2;
if (*s == '@') { /* '@' is allowed as first character */
*b++ = *s++;
bsize--;
}
if (vsc[(unsigned char)*s] == 1) { /* first character valid? */
*b++ = *s++;
while (vsc[(unsigned char)*s] && bsize--)
*b++ = *s++;
}
*b = 0;
return (s);
}
char *getarg(struct GlobalVars *gv,char *s)
/* read next argument into gv->strbuf */
{
uint8 v,*vac = valid_argchars;
char c,*b = gv->strbuf;
int bsize = STRBUFSIZE-1;
if (*s == '@') { /* '@' is allowed as first character */
*b++ = *s++;
bsize--;
}
while ((v = vac[(unsigned char)*s]) && bsize--) {
*b++ = *s++;
if (v==2) { /* string? */
c = *(s-1);
for (;;) {
if (bsize--) {
if (!(*b++ = *s)) /* string can only be terminated by EOL */
return (s);
}
else
return (s);
if (*s++ == c) {
if (*s==c || *(s-2)=='\\') {
if (bsize--) /* "", '' \" or \' don't terminate the string */
*b++ = *s++;
else
return (s);
}
else
break;
}
}
}
}
*b = 0;
return (s);
}
char *skipspaces(char *s)
/* advance string pointer to the first character, which is no white space */
{
while (*s==' ' || *s=='\t')
++s;
return (s);
}
char *remquotes(char *s)
/* remove " or ', if present and return new strbuf-pointer */
{
int len;
char c = *s;
if (c=='\"' || c=='\'') /* string is in quotes */
if (len = strlen(++s) - 1)
if (s[len] == c)
s[len] = 0;
return (s);
}
void checkEOL(char *s)
/* check for illegal extra characters on line */
{
s = skipspaces(s);
if (*s && *s!='#') /* only comment is allowed as an extra character */
error(18); /* extra characters on line */
}
char *skipexpression(struct GlobalVars *gv,char *s)
{
char c;
do {
s = getarg(gv,s);
s = skipspaces(s);
c = *s++;
}
while (c!=0 && c!='#' && c!=',');
return (--s);
}
void read_macro_params(struct GlobalVars *gv,struct ParsedLine *pl,
struct MacroParams *mp,char *op)
/* read parameters and store pointers to their first character */
{
char c;
mp->param[0] = mp->param0;
if (pl->branch_hint)
mp->param0[0] = pl->branch_hint>0 ? '+' : '-';
for (;;) {
op = skipspaces(op);
c = *op;
if (c!=0 && c!='#') { /* another parameter? */
if (mp->narg < (MAX_MACPARAMS-1)) {
mp->param[++mp->narg] = op;
op = skipexpression(gv,op);
c = *op;
if (c==0 || c=='#') {
*op = 0; /* end of line / start of comment */
return;
}
*op++ = 0; /* next parameter */
}
else {
error(11); /* too many macro parameters */
return;
}
}
else
return;
}
}
char *getexp(struct GlobalVars *gv,char *s,uint32 *val,uint8 size)
/* evaluate expression, create relocations or x-references, if required */
{
struct Expression exp;
s = eval_expression(gv,&exp,s);
if (exp.type != SYM_UNDEF) {
if (exp.reloctype==R_PPC_ADDR32 && size==2)
exp.reloctype = R_PPC_ADDR16;
switch (exp.type) {
case SYM_RELOC:
*val = makereloc(gv,&exp); /* make relocation entry */
break;
case SYM_EXTERN: /* reference to external symbol */
*val = makexref(gv,&exp,size);
break;
default: /* SYM_ABS */
*val = exp.value;
break;
}
}
else
error(19); /* undefined symbol */
return (s);
}
static uint32 hihalf(struct GlobalVars *gv,uint32 x)
{
if (gv->signedexp)
return ((int32)x>>16);
else
return (x>>16);
}
static uint32 lohalf(struct GlobalVars *gv,uint32 x)
{
if (gv->signedexp)
return ((int16)x);
else
return (x&0xffff);
}
uint32 makereloc(struct GlobalVars *gv,struct Expression *exp)
/* make new relocation entry for current section */
{
struct Reloc dummy,*reloc;
if (gv->anotherpass) {
/* don't waste memory, if another pass is required anyway */
reloc = &dummy;
}
else {
reloc = alloc(sizeof(struct Reloc));
addtail(&gv->csect->reloclist,&reloc->n);
}
reloc->relocsect = exp->symbol->relsect;
reloc->offset = gv->csect->pc;
reloc->type = exp->reloctype;
reloc->addend = exp->value;
switch (exp->reloctype) {
case R_PPC_ADDR16_HA:
case R_PPC_ADDR16_HI:
reloc->offset += 2;
return (hihalf(gv,exp->value));
case R_PPC_ADDR16_LO:
reloc->offset += 2;
return (lohalf(gv,exp->value));
case R_PPC_ADDR16:
case R_PPC_SDAREL16:
reloc->offset += 2;
return (exp->value);
case R_PPC_REL14:
case R_PPC_REL14_BRTAKEN:
case R_PPC_REL14_BRNTAKEN:
if (gv->output != OFMT_ELF)
reloc->offset += 2;
default:
return (exp->value);
}
}
uint32 makexref(struct GlobalVars *gv,struct Expression *exp,uint8 size)
{
struct XReference dummy,*xref;
if (gv->anotherpass) {
/* don't waste memory, if another pass is required anyway */
xref = &dummy;
}
else {
xref = alloc(sizeof(struct XReference));
addtail(&gv->csect->xreflist,&xref->n);
}
xref->xsymbol = exp->symbol;
xref->offset = gv->csect->pc;
xref->addend = exp->value;
xref->type = exp->reloctype;
xref->size = size;
switch (exp->reloctype) {
case R_PPC_ADDR16_HA:
case R_PPC_ADDR16_HI:
xref->offset += 2;
return (hihalf(gv,exp->value));
case R_PPC_ADDR16_LO:
xref->offset += 2;
return (lohalf(gv,exp->value));
case R_PPC_ADDR16:
case R_PPC_SDAREL16:
xref->offset += 2;
return (exp->value);
case R_PPC_REL14:
case R_PPC_REL14_BRTAKEN:
case R_PPC_REL14_BRNTAKEN:
if (gv->output != OFMT_ELF)
xref->offset += 2;
default:
return (exp->value);
}
}
char *getintexp(struct GlobalVars *gv,char *s,uint32 *val)
/* evaluate expression and check if absolute integer */
{
struct Expression exp;
s = eval_expression(gv,&exp,s);
if (exp.type != SYM_UNDEF) {
if (exp.type == SYM_ABS)
*val = exp.value;
else
error(24); /* constant integer expression required */
}
else
error(19); /* undefined symbol */
return (s);
}
char *eval_expression(struct GlobalVars *gv,struct Expression *exp_result,
char *s)
/* evaluate an integer expression and determine its type */
{
struct Expression exp[EXPSTACKSIZE];
uint8 ops[EXPSTACKSIZE-1],op,ta,tb;
int i,j,k,exp_args=0;
uint8 unary; /* unary operator, 0=none, 1=plus, 2=minus, 3=not */
char c;
char *arg=gv->strbuf;
struct Symbol *sym;
exp_result->value = 0;
exp_result->type = SYM_UNDEF;
/* build expression stack */
for (;;) {
unary = 0;
exp[exp_args].reloctype = R_PPC_ADDR32;
for (;;) {
s = skipspaces(s);
s = getarg(gv,s); /* try to get next argument */
if (c = *arg)
break; /* got it */
if ((c=*s++) == '(') { /* beginning of a term? */
s = eval_expression(gv,&exp[exp_args],s);
if (*s++ != ')') {
error(21); /* missing closing parenthesis */
s--;
}
*arg = 0; /* expression in term is already evaluated */
break;
}
if (c==0 || c=='#' || c==')') {
error(17); /* missing argument */
s--;
c = 0;
break;
}
if (unary)
error(20); /* double unary operator */
else
switch (c) {
case '+':
unary = 1;
break;
case '-':
unary = 2;
break;
case '~':
unary = 3;
break;
default:
error(13); /* syntax error */
break;
}
}
if (!c)
break; /* evaluate */
if (c = *arg) {
if (c>='0' && c<='9') { /* numeric constant */
if (c=='0')
switch (arg[1]) {
case 'x':
exp[exp_args].value = read_hex(&arg[2]);
break;
case 'b':
exp[exp_args].value = read_bin(&arg[2]);
break;
case 0:
exp[exp_args].value = 0;
break;
default:
exp[exp_args].value = read_oct(&arg[1]);
break;
}
else
exp[exp_args].value = read_dec(arg);
exp[exp_args].type = SYM_ABS;
}
else if (c=='\"' || c=='\'') { /* string constant */
exp[exp_args].value = read_str(arg);
exp[exp_args].type = SYM_ABS;
}
else { /* get symbol value */
if (sym = search_symbol(gv,arg)) {
exp[exp_args].value = sym->value;
exp[exp_args].type = sym->type;
exp[exp_args].symbol = sym;
}
else {
if (gv->pass && gv->autoextern) { /* pass 2? auto. decl. extern */
sym = add_symbol(gv,arg,SYM_EXTERN,0);
exp[exp_args].value = sym->value;
exp[exp_args].type = sym->type;
exp[exp_args].symbol = sym;
}
else {
exp[exp_args].value = 0;
exp[exp_args].type = SYM_UNDEF;
}
}
}
}
if (unary) {
if (exp[exp_args].type >= SYM_RELOC)
error(22); /* illegal operation for a relocatable expression */
else
switch (unary) {
case 2: /* negate */
exp[exp_args].value = -exp[exp_args].value;
break;
case 3: /* not */
exp[exp_args].value = ~exp[exp_args].value;
break;
}
}
if (exp[exp_args].type == SYM_ABS)
exp[exp_args].reloctype = R_NONE;
/* check for operator */
s = skipspaces(s);
c = *s;
i = 0;
while (i<NUMOPS) {
if (c == valid_operators[i])
break;
i++;
}
if (i==NUMOPS)
break; /* end of expression - evaluate */
ops[exp_args++] = i;
if (*(++s) == c)
if (i==5 || i==6) /* <<, >> */
++s;
}
/* evaluate expression stack */
for (i=MAXOPPRI; i>=0; i--) {
for (j=0; j<exp_args; ) {
op = ops[j];
if (op_priority[op] == i) {
if (!(ta = exp[j].type) || !(tb = exp[j+1].type)) {
exp[j].value = 0;
exp[j].type = SYM_UNDEF;
exp[j].reloctype = R_PPC_ADDR32;
}
else switch(op) {
case 0: /* + */
if (ta != tb) {
if (ta == SYM_ABS) {
exp[j].type = tb; /* reloc + abs = reloc */
exp[j].reloctype = exp[j+1].reloctype;
exp[j].symbol = exp[j+1].symbol;
}
else if (tb != SYM_ABS) {
error(22); /* illegal reloc operation */
break;
}
}
else if (ta >= SYM_RELOC) {
error(22);
break;
}
exp[j].value += exp[j+1].value;
break;
case 1: /* - */
if (ta != tb) { /* reloc - abs = reloc */
if (tb != SYM_ABS) {
error(22);
break;
}
}
else { /* reloc - reloc = abs | abs - abs = abs */
if (ta != SYM_EXTERN) { /* extern - extern not supported */
exp[j].type = SYM_ABS;
if (ta == SYM_RELOC)
if (exp[j].symbol->relsect != exp[j+1].symbol->relsect)
error(23); /* symbols reside in different sections */
}
else {
error(22);
break;
}
}
exp[j].value -= exp[j+1].value;
break;
case 2: /* * */
if (ta == tb == SYM_ABS)
exp[j].value *= exp[j+1].value;
else
error(22);
break;
case 3: /* / */
if (ta == tb == SYM_ABS)
exp[j].value /= exp[j+1].value;
else
error(22);
break;
case 4: /* % */
if (ta == tb == SYM_ABS)
exp[j].value %= exp[j+1].value;
else
error(22);
break;
case 5: /* << */
if (ta == tb == SYM_ABS)
exp[j].value <<= exp[j+1].value;
else
error(22);
break;
case 6: /* << */
if (ta == tb == SYM_ABS)
exp[j].value >>= exp[j+1].value;
else
error(22);
break;
case 7: /* & */
if (ta == tb == SYM_ABS)
exp[j].value &= exp[j+1].value;
else
error(22);
break;
case 8: /* | */
if (ta == tb == SYM_ABS)
exp[j].value |= exp[j+1].value;
else
error(22);
break;
case 9: /* ^ */
if (ta == tb == SYM_ABS)
exp[j].value ^= exp[j+1].value;
else
error(22);
break;
}
exp_args--;
for (k=j; k<exp_args; k++) {
memcpy(&exp[k+1],&exp[k+2],sizeof(struct Expression));
ops[k] = ops[k+1];
}
}
else
j++;
}
if (exp_args==0)
break;
}
memcpy(exp_result,&exp[0],sizeof(struct Expression));
if (*s == '@') { /* check for @xxx additions */
s++;
switch (tolower((unsigned char)*s++)) {
case 'l': /* symbol@l : lower half-word */
if (exp_result->type == SYM_ABS) {
exp_result->reloctype = R_NONE;
exp_result->value = lohalf(gv,exp_result->value);
}
else
exp_result->reloctype = R_PPC_ADDR16_LO;
break;
case 'h':
if (tolower((unsigned char)*s) == 'a') {
s++; /* symbol@ha : higher (addi) */
if (exp_result->type == SYM_ABS) {
exp_result->reloctype = R_NONE;
if (exp_result->value & 0x8000)
exp_result->value = hihalf(gv,exp_result->value + 0x10000);
else
exp_result->value = hihalf(gv,exp_result->value);
}
else
exp_result->reloctype = R_PPC_ADDR16_HA;
}
else { /* symbol@h : higher half-word */
if (exp_result->type == SYM_ABS) {
exp_result->reloctype = R_NONE;
exp_result->value = hihalf(gv,exp_result->value);
}
else
exp_result->reloctype = R_PPC_ADDR16_HI;
}
break;
case 's': /* symbol@sdarx, symbol@sdax : small data offset */
if (!strncmpnc(s,"darx",4)) {
s += 4;
exp_result->reloctype = R_PPC_SDAREL16;
}
else if (!strncmpnc(s,"dax",3)) {
s += 3;
exp_result->reloctype = R_PPC_SDAREL16;
}
else
s -= 2;
break;
default:
s -= 2;
break;
}
}
return (s);
}
static uint32 read_hex(char *s)
{
uint32 x=0,y;
while (y = (unsigned char)*s++) {
if (y > '9')
y = (y & 0x5f) - 7;
x <<= 4;
x += y - '0';
}
return (x);
}
static uint32 read_dec(char *s)
{
uint32 x=0,y;
while (y = (unsigned char)*s++) {
x *= 10;
x += y - '0';
}
return (x);
}
static uint32 read_oct(char *s)
{
uint32 x=0,y;
while (y = (unsigned char)*s++) {
x <<= 3;
x += y - '0';
}
return (x);
}
static uint32 read_bin(char *s)
{
uint32 x=0,y;
while (y = (unsigned char)*s++) {
x <<= 1;
x += y & 1;
}
return (x);
}
static uint32 read_str(char *s)
{
uint32 x=0;
char c,sc=*s++;
while (c = *s++) {
if (c==sc) {
if (*s==sc) /* "" is converted into " */
s++;
else
break;
}
else if (c=='\\') {
if (!(c = escchar(*s++)))
break;
}
x <<= 8;
x += (unsigned char)c;
}
return (x);
}